package com.sunseagear.common.base.mvc.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.sunseagear.common.base.mvc.entity.DataEntity;
import com.sunseagear.common.base.mvc.entity.TreeEntity;
import com.sunseagear.common.base.mvc.mapper.BaseTreeMapper;
import com.sunseagear.common.base.mvc.service.ITreeCommonService;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Transactional
public class TreeCommonServiceImpl<M extends BaseTreeMapper<T>, T extends TreeEntity<ID> & Serializable, ID extends Serializable>
        extends CommonServiceImpl<M, T> implements ITreeCommonService<T, ID> {


    @Override
    public List<T> selectTreeList(Wrapper<T> wrapper) {
        List<T> list = super.list(wrapper);
        return buildTree(getTreeTables(list));
    }

    @Override
    public List<ID> getParentIdsAndSelf(ID id) {
        String tableName = getTableName(getEntityClass());
        return baseMapper.getParentIdsAndSelf(tableName, id);
    }

    @Override
    public List<ID> getChildrenIdsAndSelf(ID id) {
        String tableName = getTableName(getEntityClass());
        return baseMapper.getChildrenIdsAndSelf(tableName, id);
    }

    @Override
    public List<ID> getParentIds(ID id) {
        String tableName = getTableName(getEntityClass());
        List<ID> parentIdsAndSelf = baseMapper.getParentIdsAndSelf(tableName, id);
        return parentIdsAndSelf.subList(0, parentIdsAndSelf.size() - 1);
    }

    @Override
    public List<ID> getChildrenIds(ID id) {
        String tableName = getTableName(getEntityClass());
        List<ID> childrenIdsAndSelf = baseMapper.getChildrenIdsAndSelf(tableName, id);
        return childrenIdsAndSelf.subList(0, childrenIdsAndSelf.size() - 1);
    }


    protected List<T> getTreeTables(List<T> treeNodeList) {
        return getTreeTables(treeNodeList, Comparator.comparing(DataEntity::getCreateDate));
    }

    protected List<T> getTreeTables(List<T> treeNodeList, Comparator<? super T> c) {
        List<T> treeTableListAll = list();
        HashMap<ID, T> treeTableHashMapAll = new HashMap<>();
        treeTableListAll.forEach(treeTable -> treeTableHashMapAll.put(treeTable.getId(), treeTable));
        HashMap<ID, T> treeTableHashMap = new HashMap<>();
        treeNodeList.forEach(treeNode -> {
            treeTableHashMap.put(treeNode.getId(), treeNode);
            getParentID(treeNode, treeTableHashMap, treeTableHashMapAll);
        });
        List<T> treeTableList = new ArrayList<>(treeTableHashMap.values());
        treeTableList.forEach(i -> {
            if (Objects.isNull(i.getCreateDate())) {
                i.setCreateDate(new Date());
            }
        });
        treeTableList.sort(c);
        return treeTableList;
    }

    private void getParentID(T treeTable, HashMap<ID, T> treeTableHashMap, HashMap<ID, T> treeTableHashMapAll) {
        ID parentId = treeTable.getParentId();
        if (parentId != null && !treeTableHashMap.containsKey(parentId)) {
            T node = treeTableHashMapAll.get(parentId);
            treeTableHashMap.put(parentId, node);
            getParentID(node, treeTableHashMap, treeTableHashMapAll);
        }

    }

    protected <T extends TreeEntity<M>, M extends Serializable> List<T> buildTree(List<T> treeNodes) {
        List<T> treeNodeList = new ArrayList<>();
        Map<M, T> treeEntityHashMap = treeNodes.stream().collect(Collectors.toMap(TreeEntity<M>::getId, Function.identity()));
        treeNodes.forEach(item -> treeEntityHashMap.put(item.getId(), item));
        treeNodes.forEach(item -> {
            if (item.getParentId() != null
                    && treeEntityHashMap.containsKey(item.getParentId())) {
                T treeEntity = treeEntityHashMap.get(item.getParentId());
                if (treeEntity.getChildren() == null) {
                    treeEntity.setChildren(new ArrayList<>());
                }
                treeEntity.getChildren().add(item);

            } else {
                treeNodeList.add(item);
            }
        });
        return treeNodeList;
    }
}
